package org.syntaxlisp.core.utils;

import lombok.Getter;
import lombok.Setter;

import java.util.*;
import java.util.function.Function;

public class ListTreeNode<K extends ListTreeVal, V> {

    final int layer;

    K key;

    @Setter
    @Getter
    V val;

    ListTreeNode<K, V> prevNode;

    // todo: ListTreeNode<K, V> 改为 List<ListTreeNode<K, V>>
    Map<Integer, List<ListTreeNode<K, V>>> nextNodeMap;

    ListTreeNode(K key, V val, ListTreeNode<K, V> prevNode) {
        this.key = key;
        this.val = val;
        this.prevNode = prevNode;
        this.layer = prevNode == null ? 0 : (prevNode.layer + 1);
        nextNodeMap = new HashMap<>();
    }

    public Optional<ListTreeNode<K, V>> getNextNode(K nextKey) {
        return getNextNode(nextKey.getHashKey(), nextKey.getUniqueKey());
    }

    public Optional<ListTreeNode<K, V>> getNextNode(K nextKey, Function<K, Integer> getKeyFunc) {
        return getNextNode(getKeyFunc.apply(nextKey), nextKey.getUniqueKey());
    }

    public Optional<ListTreeNode<K, V>> getNextNode (Integer keyCode, String uniqueKey) {
        List<ListTreeNode<K, V>> valList = nextNodeMap.getOrDefault(keyCode, Collections.emptyList());
        ListTreeNode<K, V> res = null;
        for (ListTreeNode<K, V> val : valList) {
            if (val.getNodeKey().getUniqueKey().equals(uniqueKey)) {
                res = val;
                break;
            }
        }
        return Optional.ofNullable(res);
    }

    public ListTreeNode<K, V> putNextNode(K key) {
        return putNextNode(key.getHashKey(), key.getUniqueKey(), key);
    }

    public ListTreeNode<K, V> putNextNode(Integer keyCode, String uniqueKey, K key) {
        Optional<ListTreeNode<K, V>> nextNode = getNextNode(keyCode, uniqueKey);
        if (nextNode.isPresent()) {
            return nextNode.get();
        }
        ListTreeNode<K, V> newNextNode = new ListTreeNode<>(key, null, this);
        List<ListTreeNode<K, V>> valList = nextNodeMap.computeIfAbsent(key.getHashKey(), k -> new ArrayList<>());
        valList.add(newNextNode);
        return newNextNode;
    }

    public ListTreeNode<K, V> putNextNode(K key, Function<K, Integer> getKeyFunc) {
        return putNextNode(getKeyFunc.apply(key), key.getUniqueKey(), key);
    }

    public ListTreeNode<K, V> putNextLeafNode(K key, V val) {
        ListTreeNode<K, V> resNode = putNextNode(key);
        if (!resNode.isLeaf()) {
            resNode.setVal(val);
        }
        return resNode;
    }

    public K getNodeKey () {
        return key;
    }

    public ListTreeNode<K, V> getPrevNode() {
        return prevNode;
    }

    public List<K> getEntireKeyList () {
        if (layer == 0) {
            return Collections.emptyList();
        }
        List<K> keyList = new ArrayList<>(layer);
        ListTreeNode<K, V> curNode = this;
        for (int i = layer - 1; i >= 0; i --) {
            keyList.add(curNode.key);
            curNode = curNode.prevNode;
        }
        Collections.reverse(keyList);
        return keyList;
    }

    public boolean isRoot() {
        return prevNode == null;
    }

    public boolean isLeaf () {
        return val != null;
    }
}
